﻿// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace SnapObjects.Data.Odbc
{
    internal class OdbcHanaFunctionParameterSchemaProvider : RoutineParameterSchemaProvider
    {
        public OdbcHanaFunctionParameterSchemaProvider(DataContext dataContext) : base(dataContext)
        {
        }

        protected override IList<DynamicModel> QueryParameters(
            RoutineNameInfo nameInfo, ISqlGenerationHelper helper)
        {
            var sql = this.BuildSchemaSql(nameInfo, helper);

            return _dataContext.SqlExecutor.Select<DynamicModel>(sql);
        }

        protected override Task<IList<DynamicModel>> QueryParametersAsync(
            RoutineNameInfo nameInfo, ISqlGenerationHelper helper, CancellationToken cancellationToken)
        {
            var sql = this.BuildSchemaSql(nameInfo, helper);

            return _dataContext.SqlExecutor.SelectAsync<DynamicModel>(sql, cancellationToken);
        }

        protected override ProcedureSchema CreateParameterSchema(
            string name,
            IReadOnlyList<AdoDbParameter> adoDbParameters,
            IReadOnlyList<DynamicModel> dbParameters)
        {
            var returnType = FunctionReturnType.Scalar;

            var dbParam = dbParameters.FirstOrDefault();

            if (dbParam != null &&
                "table".Equals(dbParam.GetValue<string>("type"), StringComparison.OrdinalIgnoreCase))
            {
                returnType = FunctionReturnType.Table;
            }

            return new ProcedureSchema(name, adoDbParameters, returnType);
        }

        private string BuildSchemaSql(RoutineNameInfo nameInfo, ISqlGenerationHelper helper)
        {
            var schema = "current_schema";

            var name = helper.RemoveDelimitIdentifier(nameInfo.Name);

            name = this.HasDelimitIdentifier(nameInfo.Name, helper) ?
                $"'{name}'" : $"UPPER('{name}')";

            if (!String.IsNullOrWhiteSpace(nameInfo.Schema))
            {
                schema = helper.RemoveDelimitIdentifier(nameInfo.Schema);

                schema = this.HasDelimitIdentifier(nameInfo.Schema, helper) ?
                    $"'{schema}'" : $"UPPER('{schema}')";
            }

            return this.GetSchemaQuerySql(schema, name);
        }

        private bool HasDelimitIdentifier(string identifier, ISqlGenerationHelper helper)
        {
            if (identifier.Length > 1)
            {
                var first = identifier.Substring(0, 1);
                var last = identifier.Substring(identifier.Length - 1, 1);

                if (helper.IsDelimitIdentifier(first, last))
                {
                    return true;
                }
            }

            return false;
        }

        private string GetSchemaQuerySql(string schema, string name)
        {
            var sql = @"
SELECT procs.FUNCTION_OID as object_uniqueidentifier,
       params.PARAMETER_NAME as parameter_name, 
       params.DATA_TYPE_NAME as parameter_datatype, 
       params.PARAMETER_TYPE as parameter_mode,
       params.LENGTH as parameter_precision,
       params.SCALE as parameter_scale,
       null as parameter_defaultvalue,
       procs.function_usage_type as type
  FROM SYS.FUNCTIONS procs 
  LEFT JOIN SYS.FUNCTION_PARAMETERS params 
    ON procs.FUNCTION_OID = params.FUNCTION_OID
 WHERE procs.SCHEMA_NAME = {0}
   AND procs.FUNCTION_NAME = {1}
 ORDER BY params.POSITION";

            return String.Format(sql, schema, name);
        }
    }
}
